#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NONSTDC_NO_DEPRECATE
#include<fstream>
#include<ctime>
#include"Util.h"
#include"StringPool.h"
#include"variable.h"
#include"ParseForestItem.h"
#include"DependencyEvaluator.h"
#include"DependencyParser.h"
using namespace std;

DependencyParser::DependencyParser(DependencyPipe *pipe, ParserOptions *options){
	this->pipe = pipe;
	this->options = options;
	// Set up arrays
	params = new Parameters((int)(pipe->dataAlphabet.size()));
	params->setLoss(*(options->lossType));
	if(options->order == 1){
		decoder = new DependencyDecoder(pipe);
	}
	else if(options->order == 2){
		if(options->model == 1){
			decoder = new DependencyDecoder2OSibling(pipe); 
		}
		if(options->model == 2){
			decoder = new DependencyDecoder2OGrandChildren(pipe);
		}
		if(options->model == 3){
			decoder = new DependencyDecoder2O(pipe);
		}
	}
	else if(options->order == 3){
		if(options->model == 1){
			decoder = new DependencyDecoder3OTSibling(pipe);
		}
		if(options->model == 2){
			decoder = new DependencyDecoder3OGSibling(pipe);
		}
		if(options->model == 3){
			decoder = new DependencyDecoder3O(pipe);
		}
	}
	else if(options->order == 4){
		if(options->model == 1){
			decoder = new DependencyDecoder4OGTSibling(pipe);
		}
	}
}

DependencyParser::~DependencyParser(){
	delete(pipe);
	delete(params);
	delete(decoder);
}

void DependencyParser::develop(std::vector<int> *instanceLengths, std::string *train_forest, std::string *devfile, std::string *logfile, std::string *goldfile){
	int i = 0;
	Parameters *params_bk = new Parameters(params->size);
	FILE *log_fp = fopen(logfile->c_str(), "w");
	int maxLength = 0;
	int numInstances = (int)(instanceLengths->size());
	for(int i = 0; i < numInstances; i++){
		if(maxLength < (*instanceLengths)[i]){
			maxLength = (*instanceLengths)[i];
		}
	}

	DependencyReader *devReader = DependencyReader::createDependencyReader(*(options->format), false, pipe->labeled, options->hasEnd);
	devReader->startReading(devfile->c_str());
	DependencyInstance *instance = devReader->getNext();
	int maxLength_dev = 0;
	while(instance != NULL){
		int length = instance->length();
		if(maxLength_dev < length){
			maxLength_dev = length;
		}
		delete(instance);
		instance = devReader->getNext();
	}
	devReader->finishReading();
	delete(devReader);

	for(i = 0; i < options->numIters; i++){
		printf("Iteration %d[", i);
		long start = clock() / CLOCKS_PER_SEC;
		developingIter(instanceLengths, maxLength, train_forest, i + 1, params_bk);
		
		int avVal = (i + 1) * ((int)(instanceLengths->size()));
		for(int j = 0; j < params->size; j++){
			params_bk->parameters[j] = params_bk->total[j] / avVal;
		}
		
		developedIter(params_bk, devfile, maxLength_dev);
		fprintf(log_fp, "Iter: %d\n", i + 1);
		string tmp = string("tmp/result.tmp");
		DependencyEvaluator::evaluate(*goldfile, tmp, *(options->format), log_fp, options->labeled);
		fprintf(log_fp, "------------------------------------------\n");
		fflush(log_fp);
		long end = clock() / CLOCKS_PER_SEC;
		printf("|Time:%ld]\n", end - start);
	}
	fclose(log_fp);
	delete(params_bk);
}



void DependencyParser::train(std::vector<int> *instanceLengths, std::string *train_forest){
	// System.out.print("About to train. ");
	// System.out.print("Num Feats: " + pipe.dataAlphabet.size());
	int maxLength = 0;
	int numInstances = (int)(instanceLengths->size());
	for(int i = 0; i < numInstances; i++){
		if(maxLength < (*instanceLengths)[i]){
			maxLength = (*instanceLengths)[i];
		}
	}
	int i = 0;
	for(i = 0; i < options->numIters; i++){
		cout<<" Iteration "<<i;
		cout<<"[";
		//system("pause");
		long start = clock() / CLOCKS_PER_SEC;
		trainingIter(instanceLengths, maxLength, train_forest, i + 1);
		long end = clock() / CLOCKS_PER_SEC;
		cout<<"|Time:"<<end - start<<"]"<<endl;
	}
	params->averageParams(i * (int)(instanceLengths->size()));
}

void DependencyParser::developingIter(std::vector<int> *instanceLengths, int maxLength, std::string *train_forest, int iter, Parameters *params_bk){

	OnlineTrainer *trainer = NULL;
	if(options->order == 1){
		trainer = new OnlineTrainer(maxLength, pipe, options, decoder, params);	
	}
	else if(options->order == 2){
		if(options->model == 1){
			trainer = new OnlineTrainer2OSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 2){
			trainer = new OnlineTrainer2OGrandChild(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 3){
			trainer = new OnlineTrainer2O(maxLength, pipe, options, decoder, params);	
		}
	}
	else if(options->order == 3){
		if(options->model == 1){
			trainer = new OnlineTrainer3OTSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 2){
			trainer = new OnlineTrainer3OGSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 3){
			trainer = new OnlineTrainer3O(maxLength, pipe, options, decoder, params);	
		}
	}
	else if(options->order == 4){
		if(options->model == 1){
			trainer = new OnlineTrainer4OGTSibling(maxLength, pipe, options, decoder, params);	
		}
	}

	trainer->developOnline(instanceLengths, train_forest, iter, params_bk);
	delete(trainer);
}
void DependencyParser::trainingIter(std::vector<int> *instanceLengths, int maxLength, std::string *train_forest, int iter){
	OnlineTrainer *trainer = NULL;
	if(options->order == 1){
		trainer = new OnlineTrainer(maxLength, pipe, options, decoder, params);	
	}
	else if(options->order == 2){
		if(options->model == 1){
			trainer = new OnlineTrainer2OSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 2){
			trainer = new OnlineTrainer2OGrandChild(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 3){
			trainer = new OnlineTrainer2O(maxLength, pipe, options, decoder, params);	
		}
	}
	else if(options->order == 3){
		if(options->model == 1){
			trainer = new OnlineTrainer3OTSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 2){
			trainer = new OnlineTrainer3OGSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 3){
			trainer = new OnlineTrainer3O(maxLength, pipe, options, decoder, params);	
		}
	}
	else if(options->order == 4){
		if(options->model == 1){
			trainer = new OnlineTrainer4OGTSibling(maxLength, pipe, options, decoder, params);	
		}
	}

	trainer->trainOnline(instanceLengths, train_forest, iter);

	delete(trainer);
}

void DependencyParser::saveModel(std::string *file){
	ObjectWriter out(file->c_str());
	out.writeArray(params->parameters, params->size);
	pipe->dataAlphabet.writeObject(out);
	pipe->typeAlphabet.writeObject(out);
}

void DependencyParser::loadModel(std::string *file){
	ObjectReader in(file->c_str());
	params->size = in.readArray(params->parameters);
	pipe->dataAlphabet.readObject(in);
	pipe->typeAlphabet.readObject(in);
	
	//pipe->closeAlphabets();
}

// ////////////////////////////////////////////////////
// Get Best Parses ///////////////////////////////////
// ////////////////////////////////////////////////////
void DependencyParser::developedIter(Parameters *params_bk, string *devfile, int maxLength){
	OnlineTrainer *trainer = NULL;
	if(options->order == 1){
		trainer = new OnlineTrainer(maxLength, pipe, options, decoder, params);	
	}
	else if(options->order == 2){
		if(options->model == 1){
			trainer = new OnlineTrainer2OSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 2){
			trainer = new OnlineTrainer2OGrandChild(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 3){
			trainer = new OnlineTrainer2O(maxLength, pipe, options, decoder, params);	
		}
	}
	else if(options->order == 3){
		if(options->model == 1){
			trainer = new OnlineTrainer3OTSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 2){
			trainer = new OnlineTrainer3OGSibling(maxLength, pipe, options, decoder, params);	
		}
		if(options->model == 3){
			trainer = new OnlineTrainer3O(maxLength, pipe, options, decoder, params);	
		}
	}
	else if(options->order == 4){
		if(options->model == 1){
			trainer = new OnlineTrainer4OGTSibling(maxLength, pipe, options, decoder, params);	
		}
	}
	trainer->developWriteLog(params_bk, devfile);
	delete(trainer);
}
void DependencyParser::outputParses(){
	string* tFile = options->testfile;
	string* file = options->outfile;

	long start = clock() / CLOCKS_PER_SEC;

	pipe->initInputFile(tFile);
	pipe->initOutputFile(file);
	cout<<"Processing Sentence: ";
	DependencyInstance* instance = pipe->nextInstance();
	int cnt = 0;
	while(instance != NULL){
		printf("%d ", cnt++);
		int length = instance->length();
		OnlineTrainer *trainer = NULL;
		if(options->order == 1){
			trainer = new OnlineTrainer(length, pipe, options, decoder, params);	
		}
		else if(options->order == 2){
			if(options->model == 1){
				trainer = new OnlineTrainer2OSibling(length, pipe, options, decoder, params);	
			}
			if(options->model == 2){
				trainer = new OnlineTrainer2OGrandChild(length, pipe, options, decoder, params);	
			}
			if(options->model == 3){
				trainer = new OnlineTrainer2O(length, pipe, options, decoder, params);	
			}
		}
		else if(options->order == 3){
			if(options->model == 1){
				trainer = new OnlineTrainer3OTSibling(length, pipe, options, decoder, params);	
			}
			if(options->model == 2){
				trainer = new OnlineTrainer3OGSibling(length, pipe, options, decoder, params);	
			}
			if(options->model == 3){
				trainer = new OnlineTrainer3O(length, pipe, options, decoder, params);	
			}
		}
		else if(options->order == 4){
			if(options->model == 1){
				trainer = new OnlineTrainer4OGTSibling(length, pipe, options, decoder, params);	
			}
		}
		trainer->outputParses(instance);
		delete(trainer);
		delete(instance);
		instance = pipe->nextInstance();
	}
	pipe->close();
	long end = clock() / CLOCKS_PER_SEC;
	cout<<endl;
	cout<<"Took: "<<(end - start)<<endl;
}

